#ifndef JLINKAL_H
#define JLINKAL_H

#include <QObject>
#include <QDebug>
#include "JLinkARMDLL.h"

static void STDCALL _JLink_errorOutHandler(const char *text)
{
    qCritical() << text;
}

static void _JLinkARM_errorOutHandler(const char *text)
{
    qCritical() << text;
}

static void STDCALL _JLink_warnOutHandler(const char *text)
{
    qWarning() << text;
}

static void _JLinkARM_warnOutHandler(const char *text)
{
    qWarning() << text;
}

class JlinkAL : public QObject
{
    Q_OBJECT
public:
    explicit JlinkAL(QObject *parent = nullptr) : QObject(parent) {

    }

    bool selectEMU(U32 SN) {
        return (JLINKARM_EMU_SelectByUSBSN(SN) >= 0);
    }

    bool open(QString* errStr = nullptr) {

        JLINK_SetErrorOutHandler(_JLink_errorOutHandler);
        JLINKARM_SetErrorOutHandler(_JLinkARM_errorOutHandler);

        JLINK_SetWarnOutHandler(_JLink_warnOutHandler);
        JLINKARM_SetWarnOutHandler(_JLinkARM_warnOutHandler);

        JLINKARM_TIF_Select(JLINKARM_TIF_SWD);
        JLINKARM_SetSpeed(4000);
        const char* errCStr = JLINKARM_Open();
        if(errCStr != NULL) {
            if(errStr != nullptr) {
                errStr->append(errCStr);
            }
            return false;
        }
        return true;
    }

    void close() {
        JLINKARM_Close();
    }

    bool setDevice(const char* deviceName) {
        char acCmd[256];
        char acOut[256];
        // Execute command "Device"
        sprintf(acCmd, "Device = %s", deviceName);
        int r = JLINKARM_ExecCommand(acCmd, acOut, sizeof(acOut));
        if (acOut[0] == 0) {// Check if command executed successfully
            // printf("Command executed successfully!\n");
            // printf("Return value of command: %d", r);
            return true;
        } else {
            // printf("Failed to execute command!\n");
            return false;
        }
    }
    bool selDevice() {
    }

    bool connect() {

        int r = JLINKARM_Connect();
        qDebug() << "connect: " << r;
        if (r < 0) {
            // printf("Could not connect to target.\n");
            return false;
        }
        return true;
    }

    int read(U32 Addr, U32 NumBytes, void* pData) {
        if(JLINKARM_EMU_IsConnected() <= 0) {
            return -255;
        }
        if(JLINKARM_IsConnected() <= 0) {
            return -256;
        }

        int numBytesRead = JLINKARM_ReadMemEx(Addr, NumBytes, pData, 0);
        return numBytesRead;
    }


    static int SN() {
        return JLINKARM_GetSN();
    }

    static int dllVersion () {
        return JLINKARM_GetDLLVersion();
    }

    // JLINKARM_DEVICE_GetIndex(const char* sDeviceName);

    static QList<int> emulatorsSNs() {
        QList<int> emusSNs;
        JLINKARM_EMU_CONNECT_INFO aConnectInfo;
        // Request emulator number
        int r = JLINKARM_EMU_GetList(JLINKARM_HOSTIF_USB, &aConnectInfo, 0);
        if(r > 0) {
            JLINKARM_EMU_CONNECT_INFO * paConnectInfo = new JLINKARM_EMU_CONNECT_INFO[r];
            JLINKARM_EMU_GetList(JLINKARM_HOSTIF_USB, paConnectInfo, r);
            for(int i=0; i<r; ++i) {
                emusSNs.append(paConnectInfo[i].SerialNumber);
            }

            delete []paConnectInfo;
        }
        return emusSNs;
    }

    static QList<JLINKARM_EMU_CONNECT_INFO> emulatorsInfos() {
        QList<JLINKARM_EMU_CONNECT_INFO> emusInfos;
        JLINKARM_EMU_CONNECT_INFO aConnectInfo;
        // Request emulator number
        int r = JLINKARM_EMU_GetList(JLINKARM_HOSTIF_USB, &aConnectInfo, 0);
        if(r > 0) {
            JLINKARM_EMU_CONNECT_INFO * paConnectInfo = new JLINKARM_EMU_CONNECT_INFO[r];
            JLINKARM_EMU_GetList(JLINKARM_HOSTIF_USB, paConnectInfo, r);
            for(int i=0; i<r; ++i) {
                emusInfos.append(paConnectInfo[i]);
            }

            delete []paConnectInfo;
        }
        return emusInfos;
    }

    static QStringList devicesNames() {

        QStringList devicesNames;
        int NumDevices = JLINKARM_DEVICE_GetInfo(-1, NULL);
        for (int i = 0; i < NumDevices; i++) {
            JLINKARM_DEVICE_INFO Info;
            Info.SizeOfStruct = sizeof(JLINKARM_DEVICE_INFO);
            JLINKARM_DEVICE_GetInfo(i, &Info);
            devicesNames.append(Info.sName);
        }
        return devicesNames;
    }
    static QList<JLINKARM_DEVICE_INFO> devicesInfos() {

        QList<JLINKARM_DEVICE_INFO> devicesInfos;
        int NumDevices = JLINKARM_DEVICE_GetInfo(-1, NULL);
        for (int i = 0; i < NumDevices; i++) {
            JLINKARM_DEVICE_INFO Info;
            Info.SizeOfStruct = sizeof(JLINKARM_DEVICE_INFO);
            JLINKARM_DEVICE_GetInfo(i, &Info);
            devicesInfos.append(Info);
        }
        return devicesInfos;
    }

signals:
};

#endif // JLINKAL_H
